package com.hongyi.util;

import lombok.SneakyThrows;
import redis.clients.jedis.*;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @author HongYi
 */
public class JedisUtil {

    /**
     * 单机模式下的Jedis连接池
     */
    private static JedisPool jedisStandalonePool;

    /**
     * 哨兵模式下的Jedis连接池
     */
    private static JedisSentinelPool jedisSentinelPool;

    /**
     * 集群模式下的Jedis连接
     */
    private static JedisCluster jedisCluster;

    /**
     * redis模型：可选 "standalone", "sentinel" 和 "cluster" 三种，不区分大小写
     */
    private static String redisMode = "standalone";

    /**
     * redis单机服务端IP地址
     */
    private static String host = "192.168.224.6";

    /**
     * redis单机服务端端口号
     */
    private static Integer port = 6379;

    /**
     * redis单机服务端连接超时毫秒数
     */
    private static Integer timeout = 10000;

    /**
     * redis单机服务端连接密码
     */
    private static String password = "";

    /**
     * 全部哨兵节点信息，格式为 "IP:PORT;IP:PORT ..."
     */
    private static String sentinelNodes = "127.0.0.1:7007;127.0.0.1:7008;127.0.0.1:7009";

    /**
     * 哨兵节点所监控的主从结构名称
     */
    private static String sentinelMasterNodes = "m7007";

    /**
     * 全部集群节点信息，格式为 "IP:PORT;IP:PORT ..."
     */
    private static String clusterNodes = "127.0.0.1:7011;127.0.0.1:7012;127.0.0.1:7013;127.0.0.1:7014;127.0.0.1:7015;127.0.0.1:7016";

    static {

        /* 创建一个Jedis连接池的配置实例
         * 最多容纳1024个连接
         * 最大等待时间10s
         * 最大空闲200
         * 最小空闲0
         * 连接耗尽时阻塞直到超时*/
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(1024);
        jedisPoolConfig.setMaxWaitMillis(10000L);
        jedisPoolConfig.setMaxIdle(200);
        jedisPoolConfig.setMinIdle(0);
        jedisPoolConfig.setBlockWhenExhausted(true);

        // 判断当前Redis模型：单机，哨兵和集群
        if ("sentinel".equalsIgnoreCase(redisMode)) {
            // 创建Jedis哨兵连接池
            Set<String> sentinels = new HashSet<>(Arrays.asList(sentinelNodes.split(";")));
            jedisSentinelPool = new JedisSentinelPool(sentinelMasterNodes, sentinels, jedisPoolConfig);
        } else if ("cluster".equalsIgnoreCase(redisMode)) {
            // 创建Jedis集群连接池
            Set<HostAndPort> nodes = new HashSet<>();
            for (String clusterNode : clusterNodes.split(";")) {
                String[] hostAndPort = clusterNode.split(":");
                nodes.add(new HostAndPort(hostAndPort[0], Integer.parseInt(hostAndPort[1])));
            }
            jedisCluster = new JedisCluster(nodes, 10000, jedisPoolConfig);
        } else {
            // 创建Jedis单机连接池
            jedisStandalonePool = new JedisPool(jedisPoolConfig, host, port, timeout, password);
        }
    }

    /**
     * 获取一个Jedis单机连接实例，若连接无法连通redis服务端则抛出异常
     *
     * @return 一个Jedis单机连接实例
     */
    public static Jedis getStandaloneJedis() {
        Jedis jedis = jedisStandalonePool.getResource();
        checkJedis(jedis);
        return jedis;
    }

    /**
     * 获取一个Jedis哨兵连接实例，若连接无法连通redis服务端则抛出异常
     */
    public static Jedis getSentinelJedis() {
        Jedis jedis = jedisSentinelPool.getResource();
        checkJedis(jedis);
        return jedis;
    }

    /**
     * 获取一个Jedis集群连接，若集群中任意一个连接无法连通服redis务端则抛出异常
     *
     * @return Jedis集群连接
     */
    public static JedisCluster getClusterJedis() {

        String currentResponse = "PONG";
        // 获取集群中所有的节点
        Map<String, JedisPool> clusterNodes = jedisCluster.getClusterNodes();

        // 遍历所有节点
        for (Map.Entry<String, JedisPool> entry : clusterNodes.entrySet()) {
            // 获取当前节点的一个Jedis连接
            Jedis jedis = entry.getValue().getResource();
            if (!currentResponse.equals(jedis.ping())) {
                throw new RuntimeException("连接redis服务端失败...");
            }
            // 释放资源
            jedis.close();
        }

        return jedisCluster;
    }

    /**
     * 关闭一个Jedis连接
     *
     * @param jedis Jedis连接
     */
    public static void closeJedis(Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }

    /**
     * 关闭一个JedisCluster连接
     *
     * @param jedisCluster JedisCluster连接
     */
    @SneakyThrows
    public static void closeJedisCluster(JedisCluster jedisCluster) {
        if (jedisCluster != null) {
            jedisCluster.close();
        }
    }

    /**
     * 检查Jedis连接是否能成功连通redis服务端，若不能则直接抛出异常
     *
     * @param jedis Jedis连接
     */
    private static void checkJedis(Jedis jedis) {
        String successResponse = "PONG";
        if (jedis == null || !successResponse.equals(jedis.ping())) {
            throw new RuntimeException("Jedis为空或Jedis连接redis服务端失败");
        }
    }
}
